#!/usr/bin/env python
# coding: utf-8

# opencv-python
import cv2
# mediapipe人工智能工具包
import mediapipe as mp
# 进度条库
from tqdm import tqdm
# 时间库
import time
# 在导入库部分添加
import numpy as np


# # 导入模型

# 导入solution
mp_hands = mp.solutions.hands
# 导入模型
hands = mp_hands.Hands(static_image_mode=False,        # 是静态图片还是连续视频帧
                       max_num_hands=1,                # 最多检测几只手
                       min_detection_confidence=0.7,   # 置信度阈值
                       min_tracking_confidence=0.5)    # 追踪阈值
# 导入绘图函数
mpDraw = mp.solutions.drawing_utils 


# # 处理单帧的函数

# 在process_frame函数前添加轨迹存储变量
# 存储食指指尖的轨迹
finger_track = []
# 创建画布
canvas = None
# 上一帧是否在绘制
last_drawing = False
# 添加握拳计时器
fist_start_time = 0
is_fist_active = False

# 在函数开始前添加一个绘制星星的函数
def draw_star(img, center, size, color):
    """在指定位置绘制一个五角星"""
    # 五角星的五个顶点
    x, y = center
    points = []
    for i in range(5):
        # 外圆上的点
        x_outer = x + size * np.cos(2 * np.pi * i / 5 - np.pi / 2)
        y_outer = y + size * np.sin(2 * np.pi * i / 5 - np.pi / 2)
        points.append([int(x_outer), int(y_outer)])
        
        # 内圆上的点
        x_inner = x + size/2 * np.cos(2 * np.pi * (i + 0.5) / 5 - np.pi / 2)
        y_inner = y + size/2 * np.sin(2 * np.pi * (i + 0.5) / 5 - np.pi / 2)
        points.append([int(x_inner), int(y_inner)])
    
    # 转换为numpy数组并绘制多边形
    points = np.array(points, np.int32)
    points = points.reshape((-1, 1, 2))
    cv2.fillPoly(img, [points], color)
    return img

def process_frame(img):
    global finger_track, canvas, last_drawing, fist_start_time, is_fist_active
    
    # 记录该帧开始处理的时间
    start_time = time.time()
    
    # 获取图像宽高
    h, w = img.shape[0], img.shape[1]
    
    # 初始化画布(如果还没有创建)
    if canvas is None:
        canvas = np.zeros((h, w, 3), dtype=np.uint8)

    # 水平镜像翻转图像，使图中左右手与真实左右手对应
    img = cv2.flip(img, 1)
    # BGR转RGB
    img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # 将RGB图像输入模型，获取预测结果
    results = hands.process(img_RGB)

    # 默认不绘制
    is_drawing = False
    show_landmarks = False
    is_deleting = False  # 新增删除模式标志
    fist_detected = False  # 当前帧是否检测到握拳

    if results.multi_hand_landmarks: # 如果有检测到手

        handness_str = ''
        index_finger_tip_str = ''
        for hand_idx in range(len(results.multi_hand_landmarks)):

            # 获取该手的21个关键点坐标
            hand_21 = results.multi_hand_landmarks[hand_idx]

            # 获取所有手指的指尖和中间关节的y坐标
            y_thumb_tip = hand_21.landmark[4].y
            y_index_tip = hand_21.landmark[8].y
            y_middle_tip = hand_21.landmark[12].y
            y_ring_tip = hand_21.landmark[16].y
            y_pinky_tip = hand_21.landmark[20].y
            
            # 获取手指PIP关节(第二关节)的y坐标，这比MCP更接近指尖
            y_index_pip = hand_21.landmark[6].y
            y_middle_pip = hand_21.landmark[10].y
            y_ring_pip = hand_21.landmark[14].y
            y_pinky_pip = hand_21.landmark[18].y
            
            # 检查手是否张开（所有手指都伸出）
            is_hand_open = (y_thumb_tip < hand_21.landmark[2].y and
                            y_index_tip < hand_21.landmark[5].y and
                            y_middle_tip < hand_21.landmark[9].y and
                            y_ring_tip < hand_21.landmark[13].y and
                            y_pinky_tip < hand_21.landmark[17].y)
            
            # 增强握拳检测 - 检查四个手指是否弯曲(指尖y坐标大于PIP关节)
            # 这种检测更宽松，可以捕捉更多样的握拳手势
            is_fist = (y_index_tip > y_index_pip and
                      y_middle_tip > y_middle_pip and
                      y_ring_tip > y_ring_pip and
                      y_pinky_tip > y_pinky_pip)
            
            # 添加一个额外检查，确认握拳时手指指尖间距较小
            finger_tips_x = [hand_21.landmark[8].x, hand_21.landmark[12].x, 
                           hand_21.landmark[16].x, hand_21.landmark[20].x]
            max_distance_x = max(finger_tips_x) - min(finger_tips_x)
            
            # 如果指尖的水平分布很集中，这更可能是握拳
            is_tightly_fisted = max_distance_x < 0.15  # 调整阈值以适应不同的握拳紧密度
            
            # 最终握拳判定：指尖弯曲且分布集中
            is_fist_gesture = is_fist and is_tightly_fisted
            
            # 检测到握拳手势
            if is_fist_gesture:
                fist_detected = True
                # 如果这是握拳的第一帧，记录开始时间
                if not is_fist_active:
                    fist_start_time = time.time()
                    is_fist_active = True
                
                # 计算握拳持续时间
                fist_duration = time.time() - fist_start_time
                
                # 握拳超过1秒才激活删除功能
                if fist_duration >= 1.0:
                    is_deleting = True
                    # 执行删除操作 - 清除画布
                    clear_canvas()
                    
                # 在画面上显示握拳计时
                countdown_text = f"Clearing in: {max(0, 1.0 - fist_duration):.1f}s"
                cv2.putText(img, countdown_text, (w//2-100, 50), 
                          cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
            else:
                is_deleting = False
            
            # 只有在手掌张开时才显示特征点
            show_landmarks = is_hand_open
            
            # 根据手势状态决定是否显示关键点
            if show_landmarks:
                # 可视化关键点及骨架连线
                mpDraw.draw_landmarks(img, hand_21, mp_hands.HAND_CONNECTIONS)
            
            # 记录左右手信息
            temp_handness = results.multi_handedness[hand_idx].classification[0].label
            handness_str += '{}:{} '.format(hand_idx, temp_handness)

            # 获取手腕根部深度坐标
            cz0 = hand_21.landmark[0].z
            
            # 只在需要显示特征点时绘制所有21个点
            if show_landmarks and not is_deleting:
                for i in range(21): # 遍历该手的21个关键点
                    # 获取3D坐标
                    cx = int(hand_21.landmark[i].x * w)
                    cy = int(hand_21.landmark[i].y * h)
                    cz = hand_21.landmark[i].z
                    depth_z = cz0 - cz
    
                    # 用圆的半径反映深度大小
                    radius = max(int(6 * (1 + depth_z*5)), 0)
    
                    if i == 0: # 手腕
                        img = cv2.circle(img,(cx,cy), radius, (0,0,255), -1)
                    if i == 8: # 食指指尖
                        img = cv2.circle(img,(cx,cy), radius, (193,182,255), -1)
                        # 将相对于手腕的深度距离显示在画面中
                        index_finger_tip_str += '{}:{:.2f} '.format(hand_idx, depth_z)
                    if i in [1,5,9,13,17]: # 指根
                        img = cv2.circle(img,(cx,cy), radius, (16,144,247), -1)
                    if i in [2,6,10,14,18]: # 第一指节
                        img = cv2.circle(img,(cx,cy), radius, (1,240,255), -1)
                    if i in [3,7,11,15,19]: # 第二指节
                        img = cv2.circle(img,(cx,cy), radius, (140,47,240), -1)
                    if i in [4,12,16,20]: # 指尖（除食指指尖）
                        img = cv2.circle(img,(cx,cy), radius, (223,155,60), -1)

            # 如果不是删除模式，才处理绘画逻辑
            if not is_deleting:
                # 检查食指绘画手势并处理绘画逻辑，不管是否显示特征点
                # 记录食指指尖位置用于绘制
                cx8 = int(hand_21.landmark[8].x * w)
                cy8 = int(hand_21.landmark[8].y * h)
                
                # 获取其他手指的指尖y坐标
                y_middle = hand_21.landmark[12].y
                y_ring = hand_21.landmark[16].y
                y_pinky = hand_21.landmark[20].y
                y_index = hand_21.landmark[8].y
                
                # 检查是否只有食指伸出（食指尖的y坐标比其他手指的y坐标小，即位置更高）
                if (y_index < y_middle - 0.05 and 
                    y_index < y_ring - 0.05 and 
                    y_index < y_pinky - 0.05):
                    # 符合绘画手势
                    is_drawing = True
                    
                    # 改为绘制金色星星 (B=0, G=215, R=255 是金黄色)
                    gold_color = (0, 215, 255)
                    draw_star(img, (cx8, cy8), 12, gold_color)
                    
                    # 只有在绘制模式下才添加轨迹点
                    if last_drawing:  # 如果上一帧也在绘制，才连接线条
                        finger_track.append((cx8, cy8))
                        # 在画布上绘制轨迹
                        if len(finger_track) > 1:
                            # 红色 (0, 0, 255)，使用纯红色，与倒计时文字颜色一致
                            cv2.line(canvas, finger_track[-2], finger_track[-1], (0, 0, 255), 12)
                    else:
                        # 开始新的绘制，添加第一个点
                        finger_track = [(cx8, cy8)]
                else:
                    # 不符合绘画手势，显示蓝色圆表示非绘制状态，不管是否显示特征点
                    img = cv2.circle(img, (cx8, cy8), 10, (255, 0, 0), -1)
        
        # 最后的文本显示部分
        end_time = time.time()
        FPS = 1/(end_time - start_time)
        
        # ========= 简化并修改字体大小的文本显示逻辑 =========
        # 这部分代码保留，它使用较小的字体显示所有必要信息
        left_margin = 25
        purple_color = (255, 0, 255)
        green_color = (0, 255, 0)
        font = cv2.FONT_HERSHEY_SIMPLEX
        font_scale = 0.8
        font_thickness = 1
        line_height = 35
        
        # 1. FPS显示（始终保留）
        fps_text = 'FPS  ' + str(int(FPS))
        img = cv2.putText(img, fps_text, (left_margin, line_height), 
                          font, font_scale, purple_color, font_thickness)
        
        if is_deleting:
            # 删除模式下只显示FPS
            pass
        elif show_landmarks:
            # 手掌张开模式下只显示手部信息和位置信息
            if handness_str:
                img = cv2.putText(img, handness_str, (left_margin, line_height*2), 
                                 font, font_scale, purple_color, font_thickness)
            
            if index_finger_tip_str:
                img = cv2.putText(img, index_finger_tip_str, (left_margin, line_height*3), 
                                 font, font_scale, purple_color, font_thickness)
        else:
            # 非张开手掌模式显示（绘画模式）
            drawing_text = "Drawing: ON" if is_drawing else "Drawing: OFF"
            img = cv2.putText(img, drawing_text, (left_margin, line_height*2), 
                             font, font_scale, green_color, font_thickness)
            
            mode_text = "Mode: Clean View"
            img = cv2.putText(img, mode_text, (left_margin, line_height*3), 
                             font, font_scale, green_color, font_thickness)
    else:
        # 如果没有检测到手，不进行绘制
        is_drawing = False
    
    # 如果当前帧没有检测到握拳，重置握拳计时器
    if not fist_detected and is_fist_active:
        is_fist_active = False
        fist_start_time = 0
    
    # 更新上一帧绘制状态
    last_drawing = is_drawing
    
    # 将画布与原图像混合，增加画布权重使轨迹更加明显
    img = cv2.addWeighted(img, 1, canvas, 0.9, 0)
    
    return img


# # 调用摄像头获取每帧（模板）

# 调用摄像头逐帧实时处理模板
# 不需修改任何代码，只需修改process_frame函数即可


# 导入opencv-python
import cv2
import time

# 获取摄像头，传入0表示获取系统默认摄像头
cap = cv2.VideoCapture(0)

# 打开cap
cap.open(0)

# 在while循环前增加清除画布的键盘响应
def clear_canvas():
    global canvas, finger_track
    if canvas is not None:
        canvas.fill(0)
    finger_track = []

# 创建全屏窗口
cv2.namedWindow('my_window', cv2.WINDOW_NORMAL)
cv2.setWindowProperty('my_window', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

# 无限循环，直到break被触发
while cap.isOpened():
    # 获取画面
    success, frame = cap.read()
    if not success:
        break
    
    ## !!!处理帧函数
    frame = process_frame(frame)
    
    # 展示处理后的三通道图像
    cv2.imshow('my_window', frame)

    key = cv2.waitKey(1)
    if key in [ord('q'), 27]: # 按键盘上的q或esc退出（在英文输入法下）
        break
    elif key == ord('c'): # 按c键清除画布
        clear_canvas()
    elif key == ord('f'): # 添加按f键切换全屏/窗口模式
        # 获取当前全屏状态
        full_screen = cv2.getWindowProperty('my_window', cv2.WND_PROP_FULLSCREEN)
        if full_screen == cv2.WINDOW_FULLSCREEN:
            # 切换到窗口模式
            cv2.setWindowProperty('my_window', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL)
        else:
            # 切换到全屏模式
            cv2.setWindowProperty('my_window', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
    
# 关闭摄像头
cap.release()

# 关闭图像窗口
cv2.destroyAllWindows()


